Repository navigation
✨ Workflow models: safe label garbage collection on rebind and model-defined human-gate replies - #17710
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of
|
|
Hive status — updated in place; edits do not notify. Full history is in the Activity Monitor. @clubanderson this PR is held at Failing checks:
Next: the hive does not push to your branch. Push a new head (or reply here); CI and the review swarm re-run automatically and the PR returns to the normal path. Evidence bundle `#17710238d1bb` sha256:ff204c2005a6e4c51711918240d7c5597f4560a02f2da06c98b7ccbbfdac8019 — download from the dashboard or `hivectl review evidence #17710`. |
Mutation testing⏱️ The 900s mutation budget ran out, so 2 package(s) were not fully mutated and the results below do not cover them:
❌ gremlins did not produce a report for 1 package(s), so no verdict can be derived from this run (see the job log):
2 mutant(s) in changed files are not reached by any test (NOT COVERED). A maintainer may apply the
Verdict: |
The stage engine provisions a bound model's label vocabulary but never removed it: unbinding a repository or rebinding it to a model with a different label set left the old labels behind forever. ensureStageLabels now remembers, per repository, which model labels it provisioned (workflows/provisioned/<owner>__<repo>.json in the workflow store) and on a hash change or unbind deletes the ones the current binding no longer declares — only when the label was provisioned by the hive, is outside the built-in stage family, is worn by no open issue or PR (one search per label; a failed search keeps it) and workflow.label_gc (default true) allows. Kept labels stay in the record so a later pass retries them; every deletion is audited as workflow_label_removed. A repository that was never bound, or a hive without the workflow store wired, makes no extra call and keeps the pre-existing behaviour. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
A bound workflow model declares `replies` per state — the verbs a human
may answer a gate with. Until now the compiler kept them but nothing
offered or accepted them; the Activity Monitor pill and `/hive`
comments only knew the built-in approve/decision/hold family.
Now, for a repository bound to a non-default model:
- `workflowmodel.Profile.RepliesForNumber` / `ReplyForNumber` resolve
the verbs for the item's current stage number and the effect of each
(the transition's `on` event, the target state, and the label
`labels.entries` maps to that state, if any).
- The `/hive` comment parser (`parseUnparkCommandFor`) tries the model's
verbs first — multi-word, case-insensitive, trailing punctuation
trimmed — and the sweep answers a parked issue with
`applyIssueModelReply`: parking labels cleared as for approve, the
mapped label applied (audited `hive_label_applied` with the reply),
and one comment that keeps the `Decision:` round-trip intact and says
what the model did with the reply. Answered replies are watermarks.
Repositories with no binding go through exactly the old parser.
- The tracker payload's `workflows.repos[<repo>].replies` carries the
verbs per stage number; `stageTrackerHumanGateVerbs` appends them
after the built-ins (deduped by label) as `wf:<verb>` actions, which
POST `{"decision":"model-reply","reply":…}` to the item decision
endpoint. The endpoint validates the verb against the bound model and
audits `repo_item_decision_model_reply`. hive-default's block and the
pill on unbound repositories are unchanged.
- The issue-unpark sweep in cmd/hive gets the stage engine's
`WorkflowFor` resolver so comment replies see the same binding the
labeller does.
Model replies are accepted on parked issues only; PR-state replies are
left for a follow-up.
Refs #17620
Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Append "Label GC and model commands (RFC phase 4, follow-up)" to the design doc — the provisioned record, the safety checks a deletion must pass, the workflow.label_gc knob, how a model's replies flow through the comment parser, the un-park sweep and the dashboard pill, and the gaps left (PR-state replies, un-parked states) — add a "Model-defined replies" section to maintainer-commands.md, and the changelog fragment. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…h a client.go citation The label a model reply applies is written because a maintainer answered the gate — `/hive <verb>` in a comment or the pill on the dashboard — so it is a solicited write for the writeback ratchet, like the rest of the un-park path. The RankActionableIssues citation in scale-envelope.md moved with the RecordWorkflowLabelRemoved helper added above it. Refs #17620 Signed-off-by: Andrew Anderson <andy@clubanderson.com>
f47c5ff to
238d1bb
Compare
|
merged by Hive trusted-author auto-merge: author @clubanderson holds role owner and GitHub permission admin on repo; CI green at 238d1bb. |
|
Thank you for your contribution! Your PR has been merged. We'd love to hear how your experience was: share feedback |
Closes two RFC phase-4 gaps of the workflow-models RFC: model labels were never removed once a repository was unbound or rebound, and a bound model's
replies(the human-gate verbs it declares per state) were never offered or accepted. P4's "done when … the change regenerates labels and commands" now holds for both halves.Refs #17620
A. Safe label garbage collection on unbind / rebind
ensureStageLabelsalready provisions a bound model'sLabelDefs. It now remembers what it provisioned and reconciles when the binding changes (pkg/github/automerge/automerge_stage_labels_gc.go):workflows/provisioned/<owner>__<repo>.jsonunderdata.workflows_dir(workflowmodel.ProvisionedLabels{repo, model, hash, labels[]}via the newProvisionedStoreinterface the workflow store implements). It is the hive's memory of the labels it created or adopted for a model.previous − current.stagelabels.LabelDefsFor+Renames); no open issue/PR wears it (label:"x" is:opensearch — a failed search keeps the label and logs);workflow.label_gcis on. Kept labels stay in the record and are retried next pass. Every deletion is auditedworkflow_label_removed.workflow.label_gc(WorkflowConfig.LabelGC *bool, default true).falsekeeps everything and logs what would have gone. Yaml-only; added tosettings_parity_allowlist.txt.B. Model-defined replies → human-gate verbs and
/hive <verb>For a repository bound to a non-default model only:
Profile.RepliesForNumber(stage)/ReplyForNumber(stage, verb)→ReplyEffect{Verb, From, To, Label}(the transition whoseonevent is the verb, its target state, and the labellabels.entriesmaps to that state, if any).NormalizeReplyfolds case/whitespace/leading slash.parseUnparkCommandFortries the model's verbs first (multi-word, longest match, trailing punctuation trimmed; rest of the line is the decision text), then the built-ins. With no verbs it is the old parser. On a parked issueapplyIssueModelReplyclears parking labels as/hive approvedoes, applies the mapped label (auditedhive_label_appliedwithreply=<verb>), and posts one comment that keeps theDecision:round-trip intact and says what the model did. Answered replies are watermarks.cmd/hivepasses the stage engine'sWorkflowForto the un-park sweep.workflows.repos[<repo>].replies(stage number → verbs; hive-default block carries none).stageTrackerHumanGateVerbsappends them after the built-ins, deduped by label, aswf:<verb>actions → prompt for an optional note →POST /api/repos/{repo}/items/{n}/decision {"decision":"model-reply","reply":…}. The handler resolves the bound profile, 409s unbound repos / verbs not offered at the item's state, 400s PRs, auditsrepo_item_decision_model_reply.C. Docs
docs/design/workflow-models.md— appended "Label GC and model commands (RFC phase 4, follow-up)".docs/maintainer-commands.md— new "Model-defined replies" section.changelog.d/added-workflowmodel-label-gc-commands.md.Tests
label_gc=false, persists/updates record, memo resets, search failure keeps label, audit emitted; unbound path unchanged.NormalizeReply, reply resolution per state, event→label match; store: provisioned record round-trip.workflowProfileForRepo, handler (unbound 409, blank 400, unknown 409, PR 400, success applies labels + audits), index.html wiring strings, node fixture forstageTrackerWorkflowReplies/stageTrackerHumanGateVerbs/stageTrackerModelReplyVerb.Coverage: automerge 88.5%, github 88.3% (floor 88), stageengine 94.0% (floor 90), workflowmodel 93.8% (floor 90), config 90.0%. Stage goldens unchanged.
pkg/dashboardpasses except the two known macOS-only failures.Known gaps
draft-review: approve / request changes) are not offered or honoured; PRs keep GitHub's native review. Un-parked states with replies are not answered.cmd/hive/main_sweeps.go(one option) andapi_repo_item_decision.go(one decision case) were touched because the pill needs a server endpoint; noapi_workflows*,openapi.jsonor Workflows-tab JS changes.